package utils.sl651;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * SL651协议通用工具类
 * 
 * 提供报文生成、编码转换、格式处理等通用功能，供项目各模块调用
 */
public class SL651CommonUtil {

    // 要素标识符与量纲的映射表（部分示例）
    private static final Map<String, String> ELEMENT_UNIT_MAP = new HashMap<>();
    static {
        ELEMENT_UNIT_MAP.put("20", "mm"); // 当前降水量
        ELEMENT_UNIT_MAP.put("26", "mm"); // 降水量累计值
        ELEMENT_UNIT_MAP.put("39", "m");  // 瞬时河道水位
        ELEMENT_UNIT_MAP.put("38", "V");  // 电源电压
    }

    /**
     * 获取SL651格式的时间字符串 (yyMMddHHmmss)
     */
    public static String getSL651Time() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss"));
    }

    /**
     * 获取SL651格式的时间字符串 (yyMMddHHmmss)
     */
    public static String getSL651Time2() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmm"));
    }
    
    /**
     * 获取当前时间字符串 (HH:mm:ss)
     */
    public static String getCurrentTime() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
    }

    /**
     * 简化的CRC16校验计算（IBM CRC-16）
     */
    public static String calculateCrc16(String hexString) {
        byte[] data = hexStringToByteArray(hexString);
        int crc = 0xFFFF;
        
        for (byte b : data) {
            crc ^= (b & 0xFF);
            for (int i = 0; i < 8; i++) {
                if ((crc & 0x0001) != 0) {
                    crc >>= 1;
                    crc ^= 0xA001;
                } else {
                    crc >>= 1;
                }
            }
        }
        
        return String.format("%04X", crc);
    }

    /**
     * 为ASCII编码报文计算CRC16校验码
     *
     * @param asciiString ASCII编码的字符串
     * @return CRC16校验码（4位十六进制字符串）
     */
    public static String calculateCrc16ForAscii(String asciiString) {
        byte[] data = asciiString.getBytes();
        int crc = 0xFFFF;

        for (byte b : data) {
            crc ^= (b & 0xFF);
            for (int i = 0; i < 8; i++) {
                if ((crc & 0x0001) != 0) {
                    crc >>= 1;
                    crc ^= 0xA001;
                } else {
                    crc >>= 1;
                }
            }
        }

        return String.format("%04X", crc);
    }
    
    /**
     * 获取SL651格式的时间字符串 (BCD格式 YYMMDDHHMMSS)
     */
    public static String getSL651TimeHex() {
        String time = getSL651Time(); // 获取 yyMMddHHmmss 格式的时间
        // 将时间字符串转换为BCD格式的十六进制表示
        StringBuilder hexTime = new StringBuilder();
        for (int i = 0; i < time.length(); i += 2) {
            // 将每两位数字转换为对应的十六进制表示
            String digitPair = time.substring(i, i + 2);
            hexTime.append(digitPair);
        }
        return hexTime.toString();
    }

    /**
     * 解析BCD编码的时间字符串为可读格式
     *
     * @param bcdTime BCD编码的时间字符串 (YYMMDDHHMMSS)
     * @return 可读的时间格式 (yyyy-MM-dd HH:mm:ss)
     */
    public static String parseBcdTime(String bcdTime) {
        if (bcdTime == null || bcdTime.length() != 12) {
            return "时间格式错误";
        }

        try {
            // 解析年份
            String yearStr = bcdTime.substring(0, 2);
            int year = Integer.parseInt(yearStr, 10);
            // 根据SL651标准，两位年份50-99表示1950-1999年，而00-49表示2000-2049年
            int fullYear = (year >= 50) ? (1900 + year) : (2000 + year);

            // 解析月份
            String monthStr = bcdTime.substring(2, 4);
            int month = Integer.parseInt(monthStr, 10);

            // 解析日期
            String dayStr = bcdTime.substring(4, 6);
            int day = Integer.parseInt(dayStr, 10);

            // 解析小时
            String hourStr = bcdTime.substring(6, 8);
            int hour = Integer.parseInt(hourStr, 10);

            // 解析分钟
            String minuteStr = bcdTime.substring(8, 10);
            int minute = Integer.parseInt(minuteStr, 10);

            // 解析秒
            String secondStr = bcdTime.substring(10, 12);
            int second = Integer.parseInt(secondStr, 10);

            return String.format("%04d-%02d-%02d %02d:%02d:%02d",
                    fullYear, month, day, hour, minute, second);
        } catch (Exception e) {
            return "时间解析错误: " + e.getMessage();
        }
    }

    /**
     * 解析BCD编码的时间字符串为可读格式
     *
     * @param bcdTime BCD编码的时间字符串 (YYMMDDHHMMSS)
     * @return 可读的时间格式 (yyyy-MM-dd HH:mm:ss)
     */
    public static String parseBcdTime2(String bcdTime) {
        if (bcdTime == null || bcdTime.length() != 10) {
            return "时间格式错误";
        }

        try {
            // 解析年份
            String yearStr = bcdTime.substring(0, 2);
            int year = Integer.parseInt(yearStr, 16);
            // 根据SL651标准，两位年份50-99表示1950-1999年，而00-49表示2000-2049年
            int fullYear = (year >= 50) ? (1900 + year) : (2000 + year);

            // 解析月份
            String monthStr = bcdTime.substring(2, 4);
            int month = Integer.parseInt(monthStr, 10);

            // 解析日期
            String dayStr = bcdTime.substring(4, 6);
            int day = Integer.parseInt(dayStr, 10);

            // 解析小时
            String hourStr = bcdTime.substring(6, 8);
            int hour = Integer.parseInt(hourStr, 10);

            // 解析分钟
            String minuteStr = bcdTime.substring(8, 10);
            int minute = Integer.parseInt(minuteStr, 10);

            // 解析
            int second = 0;

            return String.format("%04d-%02d-%02d %02d:%02d:%02d",
                    fullYear, month, day, hour, minute, 0);
        } catch (Exception e) {
            return "时间解析错误: " + e.getMessage();
        }
    }


    /**
     * 验证报文格式是否正确
     * 
     * @param message 报文字符串
     * @return 验证结果信息
     */
    public static String validateMessage(String message) {
        if (message == null || message.trim().isEmpty()) {
            return "报文为空";
        }
        
        String clean = message.replaceAll("\\s+", "").toUpperCase();
        
        // 检查起始符
        if (clean.startsWith("01")) {
            return "ASCII编码格式SL651报文 (长度: " + clean.length() + " 字符)";
        } else if (clean.startsWith("7E7E")) {
            return "HEX编码格式SL651报文 (长度: " + clean.length() + " 字符)";
        } else if (clean.matches("^[0-9A-F]+$") && clean.length() >= 90) {
            return "可能的SL651报文 (长度: " + clean.length() + " 字符)";
        } else {
            return "非SL651报文格式";
        }
    }

    /**
     * ASCII字符串转换为十六进制
     */
    public static String asciiToHex(String ascii) {
        StringBuilder hex = new StringBuilder();
        for (char c : ascii.toCharArray()) {
            hex.append(String.format("%02X", (int) c));
        }
        return hex.toString();
    }

    /**
     * 字节数组转十六进制字符串
     *
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    public static String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b & 0xFF));
        }
        return sb.toString();
    }

    /**
     * 十六进制字符串转换为字节数组
     */
    public static byte[] hexStringToByteArray(String hexString) {
        if (hexString == null) {
            throw new IllegalArgumentException("Invalid hex string");
        }
        String cleanHex = hexString.replaceAll("\\s+", "");
        if (cleanHex.length() % 2 != 0) {
            throw new IllegalArgumentException("Invalid hex string");
        }
        byte[] bytes = new byte[cleanHex.length() / 2];
        for (int i = 0; i < cleanHex.length(); i += 2) {
            bytes[i / 2] = (byte) Integer.parseInt(cleanHex.substring(i, i + 2), 16);
        }
        return bytes;
    }
    
    /**
     * 十六进制字符串转ASCII字符串
     * 
     * @param hexString 十六进制字符串
     * @return ASCII字符串
     */
    public static String hexToAscii(String hexString) {
        StringBuilder ascill = new StringBuilder();
        for (int i = 0; i < hexString.length(); i += 2) {
            String hex = hexString.substring(i, i + 2);
            int value = Integer.parseInt(hex, 16);
            ascill.append((char) value);
        }
        return ascill.toString();
    }

    /**
     * 解析BCD编码的数字字符串
     * BCD编码中每个十六进制字符代表一个十进制数字
     */
    public static int parseBcdDigit(String hexStr) {
        int result = 0;
        for (int i = 0; i < hexStr.length(); i++) {
            char c = hexStr.charAt(i);
            int digit;
            if (c >= '0' && c <= '9') {
                digit = c - '0';
            } else if (c >= 'A' && c <= 'F') {
                digit = c - 'A' + 10;
            } else {
                throw new NumberFormatException("Invalid hex character: " + c);
            }
            result = result * 10 + digit;
        }
        return result;
    }

    /**
     * 获取功能码描述
     */
    public static String getFunctionCodeDescription(String functionCode) {
        if (functionCode == null) {
            return "未知功能码";
        }
        switch (functionCode) {
            case SL651Protocol.FUNC_LINK_MAINTAIN:
                return "链路维持报文";
            case SL651Protocol.FUNC_TEST_REPORT:
                return "测试报文";
            case SL651Protocol.FUNC_TIMER_REPORT:
                return "定时报文";
            case SL651Protocol.FUNC_ADD_REPORT:
                return "加报报文";
            case SL651Protocol.FUNC_HOUR_REPORT:
                return "小时报文";
            case SL651Protocol.FUNC_ARTIFICIAL_REPORT:
                return "人工报文";
            case SL651Protocol.FUNC_PICTURE_REPORT:
                return "图像报文";
            case SL651Protocol.FUNC_WATER_PUMP:
                return "水泵电机工作状态";
            case SL651Protocol.FUNC_GATE:
                return "闸门开启高度";
            default:
                return "未知功能码";
        }
    }

    /**
     * 将字符串转换为ASCII十六进制表示
     * 例如："12345" 转换为 "3132333435"
     *
     * @param input 输入字符串
     * @return ASCII十六进制表示
     */
    public static String stringToAsciiHex(String input) {
        if (input == null) {
            return "";
        }
        
        StringBuilder result = new StringBuilder();
        for (char c : input.toCharArray()) {
            result.append(String.format("%02X", (int) c));
        }
        
        return result.toString();
    }

    /**
     * 将字符串转换为指定长度的ASCII十六进制表示，不足长度时在末尾补0
     * 例如："12345" 转换为 "3132333435"，如果指定长度为12，则返回"313233343500"
     *
     * 注意：在SL651协议ASCII编码格式中：
     * - 密码字段是2字节HEX编码转换为4个ASCII字符传输，例如"1234"
     * - 遥测站地址是5字节HEX编码转换为10个ASCII字符传输
     * - 其他字段也按照相应规则转换
     *
     * @param input 输入字符串
     * @param length 指定长度
     * @return 指定长度的ASCII十六进制表示
     */
    public static String stringToAsciiHex(String input, int length) {
        if (input == null) {
            input = "";
        }
        
        StringBuilder result = new StringBuilder();
        for (char c : input.toCharArray()) {
            result.append(String.format("%02X", (int) c));
        }
        
        // 如果结果长度不足指定长度，用0补齐
        while (result.length() < length) {
            result.append("0");
        }
        
        // 如果结果长度超过指定长度，截取前面的部分
        if (result.length() > length) {
            return result.substring(0, length);
        }
        
        return result.toString();
    }

    public static String getSL651TimeHex(String highByte, String lowByte, String identifier, int pos) {
        // 低位字节的高4位表示数据字节数，低4位表示小数点后位数
        int lengthDecimalValue = Integer.parseInt(lowByte, 16);
        int dataLengthBytes = (lengthDecimalValue >> 3) & 0x1F; // 高5位：数据长度（字节）
        int decimalPlaces = lengthDecimalValue & 0x07;         // 低3位：小数位数
        String valueHex = identifier.substring(0, dataLengthBytes);
        pos += dataLengthBytes;
        System.out.print("(字节数=" + dataLengthBytes + ", 小数位=" + decimalPlaces + ") ");
        // 3. 验证数值部分长度是否与解析的长度一致（1字节=2位十六进制）
        if (valueHex.length() != dataLengthBytes * 2) {
            throw new IllegalArgumentException("数值部分长度与定义不符：" + valueHex);
        }
        // 4. 将十六进制数值转换为十进制
        long valueDecimal = Long.parseLong(valueHex, 16);
        // 5. 计算实际数值（除以10的小数位次方）
        double actualValue = valueDecimal / Math.pow(10, decimalPlaces);
        // 6. 获取量纲
        String unit = ELEMENT_UNIT_MAP.getOrDefault(highByte, "");
        // 7. 格式化输出（保留对应小数位）
        String format = "%." + decimalPlaces + "f%s";
        return String.format(format, actualValue, unit);
    }
}